# -*- coding: utf-8 -*-
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# openUBMC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#         http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.

"""
代码发射器基础接口

定义统一的代码发射器接口和结果类型。
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import List, Dict, Any
from ...ir import IRNode
from .context import CodeGenContext
from ...ir.expressions import IRCall, IRName, IRAttribute
from ...ir.expressions import IRBinOp, IRUnaryOp, IRBoolOp, IRCompare


@dataclass
class EmitResult:
    """代码生成结果

    统一的代码生成结果类型，包含生成的代码和相关元信息。
    """

    code: str
    precedence: int = 0
    requires_includes: List[str] = field(default_factory=list)
    side_effects: List[str] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)


class BaseEmitter(ABC):
    """代码发射器基类

    所有代码发射器的基础接口，提供统一的扩展机制。
    """

    @abstractmethod
    def can_emit(self, node: IRNode, context: "CodeGenContext") -> bool:
        """检查是否能处理该节点

        Args:
            node: IR节点
            context: 代码生成上下文

        Returns:
            bool: 是否能处理该节点
        """
        pass

    @abstractmethod
    def emit(self, node: IRNode, context: "CodeGenContext") -> EmitResult | None:
        """生成代码

        Args:
            node: IR节点
            context: 代码生成上下文

        Returns:
            EmitResult | None: 生成结果，如果无法处理该节点则返回 None

        说明：
            返回 None 表示该 emitter 不处理此节点，registry 会继续尝试
            下一个 emitter。这是 emitter 链式调度的标准模式。
        """
        pass

    def get_priority(self) -> int:
        """获取发射器优先级

        优先级高的发射器会优先被选择。

        Returns:
            int: 优先级，数值越大优先级越高
        """
        return 0

    def get_name(self) -> str:
        """获取发射器名称

        Returns:
            str: 发射器名称
        """
        return self.__class__.__name__


class NodeTypeEmitter(BaseEmitter):
    """基于节点类型的发射器基类

    提供基于IR节点类型的匹配逻辑。
    """

    def __init__(self, supported_types: List[type]):
        """初始化

        Args:
            supported_types: 支持的IR节点类型列表
        """
        self.supported_types = supported_types

    def can_emit(self, node: IRNode, context: "CodeGenContext") -> bool:
        """检查节点类型是否匹配"""
        return any(isinstance(node, node_type) for node_type in self.supported_types)


class FunctionCallEmitter(BaseEmitter):
    """基于函数调用的发射器基类

    提供基于函数名的匹配逻辑，用于处理特定的函数调用。
    """

    def __init__(self, supported_functions: List[str]):
        """初始化

        Args:
            supported_functions: 支持的函数名列表
        """
        self.supported_functions = supported_functions

    def can_emit(self, node: IRNode, context: "CodeGenContext") -> bool:
        """检查是否为支持的函数调用"""

        if not isinstance(node, IRCall):
            return False

        func_name = None
        if isinstance(node.func, IRName):
            func_name = node.func.id
        elif isinstance(node.func, IRAttribute):
            func_name = node.func.attr

        return func_name in self.supported_functions


class OperatorEmitter(BaseEmitter):
    """基于操作符的发射器基类

    提供基于操作符的匹配逻辑。
    """

    def __init__(self, supported_operators: List[str]):
        """初始化

        Args:
            supported_operators: 支持的操作符列表
        """
        self.supported_operators = supported_operators

    def can_emit(self, node: IRNode, context: "CodeGenContext") -> bool:
        """检查是否为支持的操作符"""

        if isinstance(node, (IRBinOp, IRUnaryOp)):
            return node.op in self.supported_operators
        elif isinstance(node, IRBoolOp):
            return node.op in self.supported_operators
        elif isinstance(node, IRCompare):
            return any(op in self.supported_operators for op in node.ops)

        return False
